﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Windows;

namespace OpenLS.Spreadsheet.UI.Internals
{
    internal class CellClusterElement : FrameworkElement
    {
        protected readonly ClusteredCellTextControl _parent;
        private readonly CellClusterKey _key;
        private readonly Worksheet _worksheet;

        protected Worksheet Worksheet
        {
            get { return _worksheet; }
        }

        private readonly List<Cell> _cells = new List<Cell>();

        protected RangeAddress RangeAddress
        {
            get { return Key.RangeAddress; }
        }

        protected Column Column
        {
            get { return _worksheet.Columns[Key.Column]; }
        }

        protected Row Row
        {
            get { return _worksheet.Rows[Key.Row]; }
        }

        public CellClusterKey Key
        {
            get { return _key; }
        }

        public CellClusterElement(ClusteredCellTextControl parent, CellClusterKey key, Worksheet worksheet)
        {
            _parent = parent;
            _key = key;
            _worksheet = worksheet;
            foreach (Cell cell in worksheet.GetCells(RangeAddress))
            {
                AddCell(cell);
            }
            worksheet.CellAdded += WorksheetCellAdded;
            worksheet.CellsRemoved += WorksheetCellsRemoved;
#if DEBUG
       //     Application.Current.Exit += (sender, e) => GC.SuppressFinalize(this);
#endif
        }


        public void Dispose()
        {
            _worksheet.CellAdded -= WorksheetCellAdded;
            _worksheet.CellsRemoved -= WorksheetCellsRemoved;
            foreach (Cell cell in _cells)
                cell.PropertyChanged -= CellPropertyChanged;
            _cells.Clear();
#if DEBUG
            disposed = true;
#endif
        }

#if DEBUG
        private bool disposed;

        ~CellClusterElement()

        {
       //     Contract.Assert(disposed);
        }
#endif

        private void WorksheetCellAdded(object sender, CellEventArgs e)
        {
            _parent.OnCellLayoutValueChanged(e.Cell);
            Contract.Assert(Parent == null);
            if (RangeAddress.Intersects(e.Cell.GetRangeAddress()))
                AddCell(e.Cell);
        }

        private void WorksheetCellsRemoved(object sender, CellRegionEventArgs e)
        {
            _parent.OnCellLayoutValueChanged(null);
            Contract.Assert(Parent == null);
            foreach (Cell c in e.Region.GetCells(RangeAddress))
                RemoveCell(c);
        }

        private void AddCell(Cell cell)
        {
            Contract.Requires(cell != null);
            Contract.Requires(!_cells.Contains(cell));
            Contract.Assert(!Enumerable.Any(_cells, c => c.Address == cell.Address));
            _cells.Add(cell);
            cell.PropertyChanged += CellPropertyChanged;
            InvalidateSelf();
        }

        internal void InvalidateSelf()
        {
            InvalidateVisual();
        }

        private bool _overflows;
        public bool Overflows
        {
            get { return _overflows; }
            protected set
            {
                if (_overflows == value)
                    return;
                _overflows = value;
            }
        }

        private void CellPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case CellProperties.Value:
                    case CellProperties.HorizontalAlignment:
                    Contract.Assert(sender is Cell);
                    _parent.OnCellLayoutValueChanged(sender as Cell);
                    break;
            }
            InvalidateSelf();
        }

        public void RemoveCell(Cell cell)
        {
            Contract.Requires(cell != null);
            Contract.Assert(_cells.Contains(cell));
            _cells.Remove(cell);
            cell.PropertyChanged -= CellPropertyChanged;
            InvalidateSelf();
        }

        protected IEnumerable<Cell> GetCells()
        {
            return _cells;
        }
    }
}